using System;
using BepuUtilities;
using BepuPhysics;
using BepuUtilities.Collections;
#if MYCODE
using BepuUtilities.Vectors;
#else
using System.Numerics;
#endif
namespace Demos.Demos.Tanks
{
    public struct AITank
    {
        /// <summary>
        /// AI使用的控制器。
        /// </summary>
        public TankController Controller;
        /// <summary>
        /// 此AI的当前目标的索引。
        /// </summary>
        public int Target;
        /// <summary>
        /// 当前移动目标位置。
        /// </summary>
        public Vector2 MovementTarget;
        /// <summary>
        /// 坦克射击的最后一帧的索引。
        /// </summary>
        public long LastShotFrame;

        /// <summary>
        /// 人工智能的剩余生命值。当它达到零度时,水箱就会解体。
        /// </summary>
        internal int HitPoints;

        public void Update(Simulation simulation, CollidableProperty<TankDemoBodyProperties> bodyProperties, Random random, long frameIndex, in Vector2 playAreaMin, in Vector2 playAreaMax, int aiIndex, ref QuickList<AITank> aiTanks, ref int projectileCount)
        {
            ref var currentPose = ref simulation.Bodies.GetBodyReference(Controller.Tank.Body).Pose;
            // tankBodyPose=localTankBodyPose*tankPose
            // tankPose=反转(LocalTankBodyPose)*tankBodyPose
            QuaternionEx.TransformUnitY(QuaternionEx.Concatenate(QuaternionEx.Conjugate(Controller.Tank.BodyLocalOrientation), currentPose.Orientation), out var tankUp);
            if (tankUp.Y < -0.5f)
            {
                // 水箱颠倒了。别费心做任何事了。
                Controller.UpdateMovementAndAim(simulation, 0, 0, false, false, false, new Vector3(1, 0, 0));
                return;
            }
            if (Target >= aiTanks.Count || Target == aiIndex || random.NextDouble() < 1f / 250f)
            {
                // 更改目标。
                if (aiTanks.Count > 1)
                {
                    do { Target = random.Next(0, aiTanks.Count); } while (Target == aiIndex);
                }
            }
            ref var targetTank = ref aiTanks[Target];
            var targetTankBody = simulation.Bodies.GetBodyReference(targetTank.Controller.Tank.Body);
            ref var targetTankPosition = ref targetTankBody.Pose.Position;
            var currentTankPosition2D = new Vector2(currentPose.Position.X, currentPose.Position.Z);
            var targetTankPosition2D = new Vector2(targetTankPosition.X, targetTankPosition.Z);
            var currentToTargetTank2D = targetTankPosition2D - currentTankPosition2D;
            if (random.NextDouble() < 1f / 250f)
            {
                // 更改移动目标。在目标当前位置周围随机选取一个点。
                // 不过,尽量避免靠得太近-那会让所有的坦克都集中在中间。
                Vector2 movementTargetOffset;
                float offsetLengthSquared;
                do
                {
                    movementTargetOffset = new Vector2((float)(random.NextDouble() - 0.5) * 150, (float)(random.NextDouble() - 0.5) * 150);
                    offsetLengthSquared = movementTargetOffset.LengthSquared();

                }
                while (offsetLengthSquared > 150 * 150 || offsetLengthSquared < 50 * 50);
                MovementTarget = Vector2.Min(playAreaMax, Vector2.Max(playAreaMin, targetTankPosition2D + movementTargetOffset));
            }

            var offset = new Vector3(MovementTarget.X - currentTankPosition2D.X, 0, MovementTarget.Y - currentTankPosition2D.Y);
            QuaternionEx.Transform(offset, QuaternionEx.Concatenate(QuaternionEx.Conjugate(currentPose.Orientation), Controller.Tank.BodyLocalOrientation), out var localMovementOffset);

            var targetHorizontalMovementDirection = new Vector2(localMovementOffset.X, -localMovementOffset.Z);
            var targetDirectionLength = targetHorizontalMovementDirection.Length();
            targetHorizontalMovementDirection = targetDirectionLength > 1e-10f ? targetHorizontalMovementDirection / targetDirectionLength : new Vector2(0, 1);
            var turnWeight = targetHorizontalMovementDirection.Y > 0 ? targetHorizontalMovementDirection.X : targetHorizontalMovementDirection.X > 0 ? 1f : -1f;
            // 在Turn Weight>=0处将leftTrack设置为1。在转角重量为-1时,leftTrack应为-1。
            var leftTrack = MathF.Min(1f, 2f * turnWeight + 1f);
            // 在转弯重量<=0时,右轨迹=1,在转弯重量=1时,右轨迹=-1。
            var rightTrack = MathF.Min(1f, -2f * turnWeight + 1f);

            // 如果我们接近目标,减速一点。
            var speedMultiplier = Math.Min(targetDirectionLength * 0.05f, 1f);
            leftTrack *= speedMultiplier;
            rightTrack *= speedMultiplier;
            // 如果我们距离目标很远,但指向正确的方向,请进行缩放。
            var zoom = targetDirectionLength > 50 && targetHorizontalMovementDirection.Y > 0.8f;
            // 我们不会计算最佳射击方案--只需直接瞄准另一辆坦克的中间即可。很糟糕的选择,但那很好。
            ref var barrelPosition = ref simulation.Bodies.GetBodyReference(Controller.Tank.Barrel).Pose.Position;
            var barrelToTarget = targetTankPosition - barrelPosition;
            var barrelToTargetLength = barrelToTarget.Length();
            barrelToTarget = barrelToTargetLength > 1e-10f ? barrelToTarget / barrelToTargetLength : new Vector3(0, 1, 0);
            Controller.UpdateMovementAndAim(simulation, leftTrack, rightTrack, zoom, false, false, barrelToTarget);
            if (frameIndex > LastShotFrame + 60)
            {
                // 我们的目标是否相当接近目标？
                if (barrelToTargetLength > 1e-10f && barrelToTargetLength < 100)
                {
                    Controller.Tank.ComputeBarrelDirection(simulation, out var barrelDirection);
                    var dot = Vector3.Dot(barrelDirection, barrelToTarget);
                    if (dot > 0.98f)
                    {
                        Controller.Tank.Fire(simulation, bodyProperties);
                        ++projectileCount;
                    }
                }
                LastShotFrame = frameIndex;
            }
        }

    }
}
